#-*- coding: utf-8 -*-
print("===========函数的定义及调用===========")
#函数的定义及调用
#函数三要素：参数、函数体、返回值
# 函数定义 求正方形的面积
def area_of_square(length_of_side):
    square_area = pow(length_of_side,2)
    return square_area

#函数调用  函数名(参数)
area = area_of_square(5)
print (area)

#参数的传递
#形参（形式参数）：函数定义时的参数，实际上就是变量名
#实参（实际参数）：函数调用时的参数，实际上就是变量的值

#位置参数 严格按照位置顺序，用实参对形参进行赋值（关联） 一般用在参数比较少的时候
#形参与实参必须一一对应，一个不能多，一个不能少
def function(x,y,z):
    print (x,y,z)

function(1,2,3)
#关键字参数
#打破位置限制，直呼其名的进行值的传递(形参=实参）
#必须遵守实参与形参数量上的一一对应 多用在参数比较多的场合
def function1(x,y,z):
    print(x,y,z)
function1(y = 1, z = 2, x = 3)
#位置参数可以与关键字参数混合使用，但是位置参数必须放在关键字参数前面
function1(1,z=2,y=3)
#不能为同一个形参重复传值
def function2(x,y,z):
    print(x,y,z)
#function2(1, z=2, x = 3) 报错
#默认参数 在定义阶段就给形参赋值 -- 该形参的常用值
#机器学习库中类的方法里非常常见 调用函数时，可以不对该参数传值
def register(name,age,sex="male"):
    print(name,age,sex)
register("andy",18)
#也可以按正常的形参进行传值
register("linzhiling",38,"female")
#默认参数应该设置为不可变类型(数字、字符串、元组)
#不能是可变类型，比如如下所示的列表，列表的地址不发生变化，具有记忆功能
# 但是它里面的内容会发生变化
def function3(ls=[]):
    print (id(ls)) #打印ls列表的地址
    ls.append(1)
    print (id(ls))
    print (ls)

function3()
function3()
function3()
#同一个函数我们把形参改成字符串,我们可以看到每次地址都发生来变化，无记忆功能
def function4(ls="Python"):
    print (id(ls))
    ls += "3.7"
    print (id(ls))
    print (ls)

function4()
function4()
function4()
#把不确定是否会使用的参数设置成None，让参数变成可选的
def name(first_name,last_name,middle_name=None):
    if middle_name:
        return first_name+middle_name+last_name
    else:
        return first_name+last_name

print(name("da","zai"))
print(name("da","zai","jie"))
#包.可变长参数 *args
#不知道会传过来多少参数 *args 该形参必须放在参数列表道最后
def foo(x,y,z,*args):
    print (x,y,z)
    print (args)

foo(1,2,3,4,5,6)  #多余道参数，打包传递给args
#实参打散
def foo1(x,y,z,*args):
    print (x,y,z)
    print (args)
foo1(1,2,3,[4,5,6]) #[包,5,6]这个列表作为作为元组道一个元素而存在
foo1(1,2,3,*[4,5,6]) #将列表中道元组打散，作为一个个单独的元素
#可变长参数 **kwargs 它的用法和字典有关
def foo2(x,y,z, **kwargs):
    print(x,y,z)
    print(kwargs)

foo2(1,2,3, a=4,b=5,c=6)#多余的参数，以字典的形式打包传递给kwargs
#字典实参打散
def foo3(x,y,z,**kwargs):
    print (x,y,z)
    print (kwargs)

foo3(1,2,3,**{"a":4,"b":5,"c":6})
#可变长参数的组合使用
def foo4(*args,**kwargs):
    print(args)
    print(kwargs)

foo4(1,2,3,a=4,b=5,c=6)

#函数体与变量的作用域
#函数体就是一段只在函数被调用时，才会执行的代码，代码构成与其他代码并无不同
#局部变量   仅在函数体内定义和发挥作用
def multipy(x,y):
    z = x*y
    return z

multipy(2,9)
#print (z) 这里报错，因为函数执行完毕，局部变量z已经被释放掉了
#全局变量  外部定义的都是全局变量 全局变量可以在函数体直接使用
n = 3
ls = [0]
def multify1(x,y):
    z = n*x*y
    ls.append(z)
    return z
print (multify1(2,9))

#通过global在函数体内定义全局变量
def multipy2(x,y):
    global c
    c = x*y
    return c

multipy2(2,9)
print (c)

#返回值
#单个返回值
def foo5(x):
    return x**2

#多个返回值 -- 以元组的形式
def foo6(x = 1):
    return 1,x,x**2,x**3
print (foo6(3))
a,b,c,d = foo6(3)  #解包赋值
print (a,b,c,d)

#可以有多个return语句，一旦其中一个执行，代表了函数运行的结束
def is_holidy(day):
    if day in ["Sunday","Saturday"]:
        return "Is holiday"
    else:
        return "Not holiday"
    print ("lalalala damaxiya") #没机会运行

print(is_holidy("Sunday"))
print(is_holidy("Monday"))
#没有return语句，返回值为None
def foo7():
    print("我是孙悟空")

res = foo7()
print (res)



print("=================函数式编程=================")
#我们用一个例子解释一下什么是函数式编程
#小A和小B进行比赛，小A每球获胜的概率为55%，小B每球获胜的概率为45%，每局比赛，先赢21球者获胜
#假设进行 n=10000局独立的比赛，小A会获胜多少?(n较大但时候，实验结果≈真实期望)

def main():
    #主要逻辑
    prob_A, prob_B, number_of_games = get_inputs() #获取原始数据，小A每球获胜概率，小B每球获胜概率，比赛场次
    win_A,win_B = sim_n_games(prob_A,prob_B,number_of_games) #获取小A赢了多少场，小B赢了多少场
    #print_summary(win_A,win_B,number_of_games)  #将获取但结果打印输出

#输入原始数据
def get_inputs():
    #输入原始数据
    prob_A = input('请输入运动员A的每球获胜概率：')
    prob_B = round(1-prob_A,2)
    number_of_gamgs = input("请输入模拟但场次（正整数): ")
    # print("模拟比赛总次数：",number_of_gamgs)
    # print("A 选手每球获胜概率：",prob_A)
    # print("B 选手每球获胜概率：",prob_B)
    return prob_A,prob_B,number_of_gamgs

#单元测试
prob_A,prob_B,number_of_games = get_inputs()
#print (prob_A,prob_B,number_of_games)

#多场比赛模拟
def sim_n_games(prob_A,prob_B,number_of_gmaes):
    #模拟多场比赛但结果
    win_A,win_B = 0,0  #初始化A，B获胜但场次
    for i in range(number_of_games):
        score_A, score_B = sim_one_game(prob_A,prob_B) #获得模拟一次比赛但比分
        if score_A > score_B:
            win_A += 1
        else:
            win_B += 1

    return win_A,win_B

import random
def sim_one_game(prob_A,prob_B):
    #模拟一场比赛但结果
    score_A,score_B = 0,0
    while not game_over(score_A,score_B):
        if random.random() < prob_A:
            score_A += 1
        else:
            score_B += 1
    return score_A,score_B

def game_over(score_A,score_B):
    return  score_A == 21 or score_B == 21


#单元测试 用 asser---断言
#表达式结果为false但时候触发异常
assert game_over(21,8) == True

if __name__ == "__main__":
    main()


print("==============匿名函数===============")
#匿名函数 基本形式 lambda 变量：函数体
#常用用法 在参数列表中最适合使用匿名函数 尤其是 key = 搭配
#排序sort()  sorted()
ls = [(93,88),(79,100),(86,71),(85,85),(76,94)] #默认根据每一个元组的第一个元素进行升序排列
ls.sort()
print(ls)
#如果我们想要根据每个元组但第二个元素进行排序，则可以使用匿名函数完成
ls.sort(key = lambda x:x[1]) #按照key这个元素设置的量进行排序，每个元素x也就是可迭代对象里的单个元素，根据每个元素的第一个位置(第2个元素)的值进行排序
print (ls)

ls1 = [(93,88),(79,100),(86,71),(85,85),(76,94)]
temp = sorted(ls,key=lambda x:x[0]+x[1],reverse=True) #根据总成绩进行排序
print (temp)
#max() min()
ls2 = [(93,88),(79,100),(86,71),(85,85),(76,94)]
n = max(ls,key=lambda x:x[1]) #找到第二个元素的最大值
print (n)
n = min(ls,key=lambda x:x[1]) #找到第二个元素的最小值
print (n)

print ("============面向对象和面向过程=============")
#面向过程 已过程为中心的编程思想，以“什么正在发生”为主要目标进行编程。 冰冷的 程序化的
#面向对象  将现实生活中的事物抽象成对象，更关注"谁在受影响"，更加贴近现实 有血有肉，拟人的（物）化的








